package model

import (
	"math"
	"time"
	"wxpay/pkg/util"

	"gorm.io/datatypes"
)

/*
DB Table Details
-------------------------------------


Table: message
[ 0] id                                             INT8                 null: false  primary: true   isArray: false  auto: false  col: INT8            len: -1      default: []
[ 1] topic                                          VARCHAR              null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: -1      default: []
[ 2] content                                        JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
[ 3] status                                         VARCHAR              null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: -1      default: []
[ 4] create_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
[ 5] update_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []


JSON Sample
-------------------------------------
{    "update_time": "2242-02-14T03:14:04.304399991+08:00",    "id": 1,    "topic": "cmUBJnArrFUpPRknLTJSGPTGV",    "content": "GiaqxxlpMIImNqwdGQgujvvSc",    "status": "yQVSGnCFjMFegFqbLQqyRfXpb",    "create_time": "2140-02-16T05:26:13.503748101+08:00"}



*/

// Message struct is a row record of the message table in the wxpay database
type Message struct {
	//[ 0] id                                             INT8                 null: false  primary: true   isArray: false  auto: false  col: INT8            len: -1      default: []
	ID int64 `gorm:"primary_key;column:id;type:INT8;"`
	//[ 1] topic                                          VARCHAR              null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: -1      default: []
	Topic string `gorm:"column:topic;type:VARCHAR;"`
	//[ 2] content                                        JSONB                null: false  primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
	Content datatypes.JSON `gorm:"column:content;type:JSONB;"`
	//[ 3] status                                         VARCHAR              null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: -1      default: []
	Status string `gorm:"column:status;type:VARCHAR;"`
	//[ 4] create_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	CreateTime time.Time `gorm:"column:create_time;type:TIMESTAMPTZ;"`
	//[ 5] update_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	UpdateTime time.Time `gorm:"column:update_time;type:TIMESTAMPTZ;"`
	//[ 6] eta   "estimate time arrive"
	ETA time.Time `gorm:"column:eta;type:TIMESTAMPTZ;"`
	//[ 7] retry_count
	RetryCount int32 `gorm:"column:retry_count;type:INT4;"`
}

// TableName sets the insert table name for this struct type
func (m *Message) TableName() string {
	return "message"
}

const MessageCreatedStatus string = "created"
const MessagePublishingStatus string = "publishing"
const MessagePublishedStatus string = "published"
const MessageFailedStatus string = "failed"
const MaxRetryCount = 10

// 消息是否达到预期发送时间
func (m *Message) IsTimeYet() bool {
	return m.ETA.Before(util.GetNow())
}

// 消息是否为待发送状态
func (m *Message) IsMessageCreatedStatus() bool {
	return m.Status == MessageCreatedStatus
}

// 消息是否达到最大重试次数
func (m *Message) ReachMaxRetryCount() bool {
	return m.RetryCount >= MaxRetryCount
}

// 消息是否为发送中状态
func (m *Message) IsMessagePublishingStatus() bool {
	return m.Status == MessagePublishingStatus
}

// 消息是否为发送完成状态
func (m *Message) IsMessagePublishedStatus() bool {
	return m.Status == MessagePublishedStatus
}

// 计算下一次投递时间
func (m *Message) GetNextETA() time.Time {
	switch m.RetryCount {
	case 1, 2, 3:
		return m.ETA.Add(20 * time.Second)
	default:
		return m.ETA.Add(time.Duration(math.Pow(2, float64(m.RetryCount-3))) * 30 * time.Second)
	}
}

func NewMessage(topic string, body []byte, d time.Duration) *Message {
	n := util.GetNow()
	return &Message{
		Topic:      topic,
		Content:    body,
		Status:     MessageCreatedStatus,
		CreateTime: n,
		UpdateTime: n,
		ETA:        n.Add(d),
	}
}
